Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Node / ReticulumProjects / MeshChatX.git / files / tests / backend / test_memory_leak_fixes.py

Displaying Raw • Download


tests/backend/test_memory_leak_fixes.py af3a1bfe874bd7c916ac83d217882c812c80f18f (af3a1bfe) Text, 14.29 KB

T8b949e"""Tests for the memory-leak fixes applied to websocket_broadcast,
update_lxmf_message_state, AsyncUtils.run_async future tracking,
nomadnet link cache sweeping, and the telemetry warning cap.
"""

Tff7b72import T7ee787asyncio
Tff7b72import T7ee787gc
Tff7b72import T7ee787json
Tff7b72import T7ee787threading
Tff7b72from T7ee787unittestT7ee787.T7ee787mock Tff7b72import Te6edf3AsyncMockTb4b4b4, Te6edf3MagicMockTb4b4b4, Te6edf3patch

Tff7b72import T7ee787pytest
Tff7b72import T7ee787RNS

Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787async_utils Tff7b72import Te6edf3AsyncUtils
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787databaseT7ee787.T7ee787messages Tff7b72import Te6edf3MessageDAO
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787nomadnet_downloader Tff7b72import Tb4b4b4(
Te6edf3_nomadnet_links_lockTb4b4b4,
Te6edf3nomadnet_cached_linksTb4b4b4,
Te6edf3sweep_stale_linksTb4b4b4,
Tb4b4b4)


T8b949e# ---------------------------------------------------------------------------
T8b949e# MessageDAO.update_lxmf_message_state
T8b949e# ---------------------------------------------------------------------------


Tff7b72class T56d364TestUpdateLxmfMessageStateTb4b4b4:
Tf0883e@pytestTff7b72.Td2a8fffixture
Tff7b72def Td2a8ffdaoTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3provider Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3MessageDAOTb4b4b4(Te6edf3providerTb4b4b4)Tb4b4b4, Te6edf3provider

Tff7b72def Td2a8fftest_update_issues_correct_sqlTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3daoTb4b4b4)Tb4b4b4:
Te6edf3message_daoTb4b4b4, Te6edf3provider Tff7b72= Te6edf3dao
Te6edf3message_daoTff7b72.Td2a8ffupdate_lxmf_message_stateTb4b4b4(
Te6edf3message_hashTff7b72=Ta5d6ff"Ta5d6ffabc123Ta5d6ff"Tb4b4b4,
Te6edf3stateTff7b72=Ta5d6ff"Ta5d6ffdeliveredTa5d6ff"Tb4b4b4,
Te6edf3progressTff7b72=T79c0ff100.0Tb4b4b4,
Te6edf3delivery_attemptsTff7b72=T79c0ff3Tb4b4b4,
Te6edf3next_delivery_attempt_atTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3rssiTff7b72=Tff7b72-T79c0ff55Tb4b4b4,
Te6edf3snrTff7b72=T79c0ff6.5Tb4b4b4,
Te6edf3qualityTff7b72=T79c0ff4Tb4b4b4,
Tb4b4b4)
Te6edf3providerTff7b72.Td2a8ffexecuteTff7b72.Td2a8ffassert_called_onceTb4b4b4(Tb4b4b4)
Te6edf3queryTb4b4b4, Te6edf3params Tff7b72= Te6edf3providerTff7b72.Td2a8ffexecuteTff7b72.Td2a8ffcall_argsTb4b4b4[T79c0ff0Tb4b4b4]
Tff7b72assert Ta5d6ff"Ta5d6ffUPDATE lxmf_messages SETTa5d6ff" Tff7b72in Te6edf3query
Tff7b72assert Ta5d6ff"Ta5d6ffWHERE hash = ?Ta5d6ff" Tff7b72in Te6edf3query
Tff7b72assert Te6edf3paramsTb4b4b4[Tff7b72-T79c0ff1Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6ffabc123Ta5d6ff"
Tff7b72assert Te6edf3paramsTb4b4b4[T79c0ff0Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6ffdeliveredTa5d6ff"
Tff7b72assert Te6edf3paramsTb4b4b4[T79c0ff1Tb4b4b4] Tff7b72== T79c0ff100.0
Tff7b72assert Te6edf3paramsTb4b4b4[T79c0ff2Tb4b4b4] Tff7b72== T79c0ff3

Tff7b72def Td2a8fftest_update_with_none_optionalsTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3daoTb4b4b4)Tb4b4b4:
Te6edf3message_daoTb4b4b4, Te6edf3provider Tff7b72= Te6edf3dao
Te6edf3message_daoTff7b72.Td2a8ffupdate_lxmf_message_stateTb4b4b4(
Te6edf3message_hashTff7b72=Ta5d6ff"Ta5d6ffxyzTa5d6ff"Tb4b4b4,
Te6edf3stateTff7b72=Ta5d6ff"Ta5d6ffsendingTa5d6ff"Tb4b4b4,
Te6edf3progressTff7b72=T79c0ff50.0Tb4b4b4,
Te6edf3delivery_attemptsTff7b72=T79c0ff1Tb4b4b4,
Te6edf3next_delivery_attempt_atTff7b72=Tff7b72NoneTb4b4b4,
Tb4b4b4)
Te6edf3_Tb4b4b4, Te6edf3params Tff7b72= Te6edf3providerTff7b72.Td2a8ffexecuteTff7b72.Td2a8ffcall_argsTb4b4b4[T79c0ff0Tb4b4b4]
Tff7b72assert Te6edf3paramsTb4b4b4[T79c0ff4Tb4b4b4] Tff7b72is Tff7b72None T8b949e# rssi
Tff7b72assert Te6edf3paramsTb4b4b4[T79c0ff5Tb4b4b4] Tff7b72is Tff7b72None T8b949e# snr
Tff7b72assert Te6edf3paramsTb4b4b4[T79c0ff6Tb4b4b4] Tff7b72is Tff7b72None T8b949e# quality

Tff7b72def Td2a8fftest_update_does_not_touch_fields_columnTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3daoTb4b4b4)Tb4b4b4:
T8b949e"""The lightweight path must never rewrite the fields/content columns."""
Te6edf3message_daoTb4b4b4, Te6edf3provider Tff7b72= Te6edf3dao
Te6edf3message_daoTff7b72.Td2a8ffupdate_lxmf_message_stateTb4b4b4(
Te6edf3message_hashTff7b72=Ta5d6ff"Ta5d6ffhTa5d6ff"Tb4b4b4,
Te6edf3stateTff7b72=Ta5d6ff"Ta5d6ffsentTa5d6ff"Tb4b4b4,
Te6edf3progressTff7b72=T79c0ff99.0Tb4b4b4,
Te6edf3delivery_attemptsTff7b72=T79c0ff2Tb4b4b4,
Te6edf3next_delivery_attempt_atTff7b72=Tff7b72NoneTb4b4b4,
Tb4b4b4)
Te6edf3query Tff7b72= Te6edf3providerTff7b72.Td2a8ffexecuteTff7b72.Td2a8ffcall_argsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[T79c0ff0Tb4b4b4]
Tff7b72assert Ta5d6ff"Ta5d6fffieldsTa5d6ff" Tff7b72not Tff7b72in Te6edf3queryTff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffsplitTb4b4b4(Ta5d6ff"Ta5d6ffsetTa5d6ff"Tb4b4b4)Tb4b4b4[T79c0ff1Tb4b4b4]Tff7b72.Td2a8ffsplitTb4b4b4(Ta5d6ff"Ta5d6ffwhereTa5d6ff"Tb4b4b4)Tb4b4b4[T79c0ff0Tb4b4b4]
Tff7b72assert Ta5d6ff"Ta5d6ffcontentTa5d6ff" Tff7b72not Tff7b72in Te6edf3queryTff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffsplitTb4b4b4(Ta5d6ff"Ta5d6ffsetTa5d6ff"Tb4b4b4)Tb4b4b4[T79c0ff1Tb4b4b4]Tff7b72.Td2a8ffsplitTb4b4b4(Ta5d6ff"Ta5d6ffwhereTa5d6ff"Tb4b4b4)Tb4b4b4[T79c0ff0Tb4b4b4]

Tff7b72def Td2a8fftest_full_roundtrip_via_real_dbTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3tmp_pathTb4b4b4)Tb4b4b4:
T8b949e"""Insert with upsert, then update state only, verify fields unchanged."""
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787database Tff7b72import Te6edf3Database

Te6edf3db_path Tff7b72= Tffa657strTb4b4b4(Te6edf3tmp_path Tff7b72/ Ta5d6ff"Ta5d6fftest.dbTa5d6ff"Tb4b4b4)
Te6edf3db Tff7b72= Te6edf3DatabaseTb4b4b4(Te6edf3db_pathTb4b4b4)
Te6edf3dbTff7b72.Td2a8ffinitializeTb4b4b4(Tb4b4b4)

Te6edf3big_fields Tff7b72= Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffimageTa5d6ff"Tb4b4b4: Tb4b4b4{Ta5d6ff"Ta5d6ffimage_bytesTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffATa5d6ff" Tff7b72* T79c0ff5000Tb4b4b4}Tb4b4b4}Tb4b4b4)
Te6edf3msg Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffdeadbeefTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffsource_hashTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffsrcTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffdstTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffpeer_hashTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffsrcTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffstateTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffoutboundTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffprogressTa5d6ff"Tb4b4b4: T79c0ff0.0Tb4b4b4,
Ta5d6ff"Ta5d6ffis_incomingTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffmethodTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffdirectTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdelivery_attemptsTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffnext_delivery_attempt_atTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6fftitleTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffhiTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffhelloTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fffieldsTa5d6ff"Tb4b4b4: Te6edf3big_fieldsTb4b4b4,
Ta5d6ff"Ta5d6fftimestampTa5d6ff"Tb4b4b4: T79c0ff12345Tb4b4b4,
Ta5d6ff"Ta5d6ffrssiTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffsnrTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffqualityTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffis_spamTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffreply_to_hashTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffattachments_strippedTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Tb4b4b4}
Te6edf3dbTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffupsert_lxmf_messageTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3dbTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffupdate_lxmf_message_stateTb4b4b4(
Te6edf3message_hashTff7b72=Ta5d6ff"Ta5d6ffdeadbeefTa5d6ff"Tb4b4b4,
Te6edf3stateTff7b72=Ta5d6ff"Ta5d6ffdeliveredTa5d6ff"Tb4b4b4,
Te6edf3progressTff7b72=T79c0ff100.0Tb4b4b4,
Te6edf3delivery_attemptsTff7b72=T79c0ff2Tb4b4b4,
Te6edf3next_delivery_attempt_atTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3rssiTff7b72=Tff7b72-T79c0ff60Tb4b4b4,
Te6edf3snrTff7b72=T79c0ff7.0Tb4b4b4,
Te6edf3qualityTff7b72=T79c0ff5Tb4b4b4,
Tb4b4b4)

Te6edf3row Tff7b72= Te6edf3dbTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffget_lxmf_message_by_hashTb4b4b4(Ta5d6ff"Ta5d6ffdeadbeefTa5d6ff"Tb4b4b4)
Tff7b72assert Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffstateTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6ffdeliveredTa5d6ff"
Tff7b72assert Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffprogressTa5d6ff"Tb4b4b4] Tff7b72== T79c0ff100.0
Tff7b72assert Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffdelivery_attemptsTa5d6ff"Tb4b4b4] Tff7b72== T79c0ff2
Tff7b72assert Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffrssiTa5d6ff"Tb4b4b4] Tff7b72== Tff7b72-T79c0ff60
Te6edf3stored_fields Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadsTb4b4b4(Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6fffieldsTa5d6ff"Tb4b4b4]Tb4b4b4)
Tff7b72assert Te6edf3stored_fieldsTb4b4b4[Ta5d6ff"Ta5d6ffimageTa5d6ff"Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffimage_bytesTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6ffATa5d6ff" Tff7b72* T79c0ff5000

Te6edf3dbTff7b72.Td2a8ffclose_allTb4b4b4(Tb4b4b4)


T8b949e# ---------------------------------------------------------------------------
T8b949e# sweep_stale_links
T8b949e# ---------------------------------------------------------------------------


Tff7b72class T56d364TestSweepStaleLinksTb4b4b4:
Tf0883e@pytestTff7b72.Td2a8fffixtureTb4b4b4(Te6edf3autouseTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72def Td2a8ff_clear_cacheTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72with Te6edf3_nomadnet_links_lockTb4b4b4:
Te6edf3nomadnet_cached_linksTff7b72.Td2a8ffclearTb4b4b4(Tb4b4b4)
Tff7b72yield
Tff7b72with Te6edf3_nomadnet_links_lockTb4b4b4:
Te6edf3nomadnet_cached_linksTff7b72.Td2a8ffclearTb4b4b4(Tb4b4b4)

Tff7b72def Td2a8fftest_sweep_removes_inactive_linksTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3active Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3activeTff7b72.Td2a8ffstatus Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffACTIVE
Te6edf3dead Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3deadTff7b72.Td2a8ffstatus Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffCLOSED

Tff7b72with Te6edf3_nomadnet_links_lockTb4b4b4:
Te6edf3nomadnet_cached_linksTb4b4b4[Ta5d6ffbTa5d6ff"Ta5d6ffaTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3active
Te6edf3nomadnet_cached_linksTb4b4b4[Ta5d6ffbTa5d6ff"Ta5d6ffbTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3dead

Te6edf3sweep_stale_linksTb4b4b4(Tb4b4b4)

Tff7b72with Te6edf3_nomadnet_links_lockTb4b4b4:
Tff7b72assert Ta5d6ffbTa5d6ff"Ta5d6ffaTa5d6ff" Tff7b72in Te6edf3nomadnet_cached_links
Tff7b72assert Ta5d6ffbTa5d6ff"Ta5d6ffbTa5d6ff" Tff7b72not Tff7b72in Te6edf3nomadnet_cached_links

Tff7b72def Td2a8fftest_sweep_empty_cache_is_noopTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3sweep_stale_linksTb4b4b4(Tb4b4b4)
Tff7b72with Te6edf3_nomadnet_links_lockTb4b4b4:
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3nomadnet_cached_linksTb4b4b4) Tff7b72== T79c0ff0

Tff7b72def Td2a8fftest_sweep_all_active_keeps_allTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72for Te6edf3i Tff7b72in Tffa657rangeTb4b4b4(T79c0ff5Tb4b4b4)Tb4b4b4:
Te6edf3link Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3linkTff7b72.Td2a8ffstatus Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffACTIVE
Tff7b72with Te6edf3_nomadnet_links_lockTb4b4b4:
Te6edf3nomadnet_cached_linksTb4b4b4[Tffa657bytesTb4b4b4(Tb4b4b4[Te6edf3iTb4b4b4]Tb4b4b4)Tb4b4b4] Tff7b72= Te6edf3link

Te6edf3sweep_stale_linksTb4b4b4(Tb4b4b4)

Tff7b72with Te6edf3_nomadnet_links_lockTb4b4b4:
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3nomadnet_cached_linksTb4b4b4) Tff7b72== T79c0ff5

Tff7b72def Td2a8fftest_sweep_thread_safetyTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72for Te6edf3i Tff7b72in Tffa657rangeTb4b4b4(T79c0ff20Tb4b4b4)Tb4b4b4:
Te6edf3link Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3linkTff7b72.Td2a8ffstatus Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffCLOSED Tff7b72if Te6edf3i Tff7b72% T79c0ff2 Tff7b72else Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffACTIVE
Tff7b72with Te6edf3_nomadnet_links_lockTb4b4b4:
Te6edf3nomadnet_cached_linksTb4b4b4[Tffa657bytesTb4b4b4(Tb4b4b4[Te6edf3iTb4b4b4]Tb4b4b4)Tb4b4b4] Tff7b72= Te6edf3link

Te6edf3errors Tff7b72= Tb4b4b4[Tb4b4b4]

Tff7b72def Td2a8ffworkerTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3sweep_stale_linksTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3errorsTff7b72.Td2a8ffappendTb4b4b4(Te6edf3eTb4b4b4)

Te6edf3threads Tff7b72= Tb4b4b4[Te6edf3threadingTff7b72.Td2a8ffThreadTb4b4b4(Te6edf3targetTff7b72=Te6edf3workerTb4b4b4) Tff7b72for Te6edf3_ Tff7b72in Tffa657rangeTb4b4b4(T79c0ff8Tb4b4b4)Tb4b4b4]
Tff7b72for Te6edf3t Tff7b72in Te6edf3threadsTb4b4b4:
Te6edf3tTff7b72.Td2a8ffstartTb4b4b4(Tb4b4b4)
Tff7b72for Te6edf3t Tff7b72in Te6edf3threadsTb4b4b4:
Te6edf3tTff7b72.Td2a8ffjoinTb4b4b4(Tb4b4b4)

Tff7b72assert Tff7b72not Te6edf3errors
Tff7b72with Te6edf3_nomadnet_links_lockTb4b4b4:
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3nomadnet_cached_linksTb4b4b4) Tff7b72== T79c0ff10


T8b949e# ---------------------------------------------------------------------------
T8b949e# AsyncUtils.run_async future tracking
T8b949e# ---------------------------------------------------------------------------


Tff7b72class T56d364TestAsyncUtilsFutureTrackingTb4b4b4:
T8b949e"""Tests operate directly on the real AsyncUtils class internals,
bypassing the conftest global mock for the tracking logic.
"""

Tf0883e@pytestTff7b72.Td2a8fffixtureTb4b4b4(Te6edf3autouseTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72def Td2a8ff_resetTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ff_pending_futures Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72yield
Te6edf3AsyncUtilsTff7b72.Td2a8ff_pending_futures Tff7b72= Tb4b4b4[Tb4b4b4]

Tff7b72def Td2a8fftest_future_appended_on_scheduleTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3mock_loop Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3mock_loopTff7b72.Td2a8ffis_runningTff7b72.Td2a8ffreturn_value Tff7b72= Tff7b72True
Te6edf3future Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3futureTff7b72.Td2a8ffdoneTff7b72.Td2a8ffreturn_value Tff7b72= Tff7b72False

Te6edf3real_main_loop Tff7b72= Te6edf3AsyncUtilsTff7b72.Td2a8ffmain_loop
Te6edf3real_run_async Tff7b72= Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTff7b72.Td2a8ff__func__

Tff7b72tryTb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ffmain_loop Tff7b72= Te6edf3mock_loop
Tff7b72with Te6edf3patchTb4b4b4(
Ta5d6ff"Ta5d6ffmeshchatx.src.backend.async_utils.asyncio.run_coroutine_threadsafeTa5d6ff"Tb4b4b4,
Te6edf3return_valueTff7b72=Te6edf3futureTb4b4b4,
Tb4b4b4)Tb4b4b4:
Te6edf3coro Tff7b72= Te6edf3asyncioTff7b72.Td2a8ffcoroutineTb4b4b4(Tff7b72lambdaTb4b4b4: Tff7b72NoneTb4b4b4)Tb4b4b4(Tb4b4b4)
Te6edf3real_run_asyncTb4b4b4(Te6edf3coroTb4b4b4)
Tff7b72finallyTb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ffmain_loop Tff7b72= Te6edf3real_main_loop

Tff7b72assert Te6edf3future Tff7b72in Te6edf3AsyncUtilsTff7b72.Td2a8ff_pending_futures

Tff7b72def Td2a8fftest_completed_futures_swept_at_thresholdTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3done Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3doneTff7b72.Td2a8ffdoneTff7b72.Td2a8ffreturn_value Tff7b72= Tff7b72True
Te6edf3pending Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3pendingTff7b72.Td2a8ffdoneTff7b72.Td2a8ffreturn_value Tff7b72= Tff7b72False

Te6edf3AsyncUtilsTff7b72.Td2a8ff_pending_futures Tff7b72= Tb4b4b4(
Tb4b4b4[Te6edf3doneTb4b4b4] Tff7b72* Tb4b4b4(Te6edf3AsyncUtilsTff7b72.Td2a8ff_FUTURES_SWEEP_THRESHOLD Tff7b72- T79c0ff2Tb4b4b4) Tff7b72+ Tb4b4b4[Te6edf3pendingTb4b4b4]
Tb4b4b4)

Te6edf3mock_loop Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3mock_loopTff7b72.Td2a8ffis_runningTff7b72.Td2a8ffreturn_value Tff7b72= Tff7b72True
Te6edf3new_future Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3new_futureTff7b72.Td2a8ffdoneTff7b72.Td2a8ffreturn_value Tff7b72= Tff7b72False

Te6edf3real_main_loop Tff7b72= Te6edf3AsyncUtilsTff7b72.Td2a8ffmain_loop
Te6edf3real_run_async Tff7b72= Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTff7b72.Td2a8ff__func__

Tff7b72tryTb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ffmain_loop Tff7b72= Te6edf3mock_loop
Tff7b72with Te6edf3patchTb4b4b4(
Ta5d6ff"Ta5d6ffmeshchatx.src.backend.async_utils.asyncio.run_coroutine_threadsafeTa5d6ff"Tb4b4b4,
Te6edf3return_valueTff7b72=Te6edf3new_futureTb4b4b4,
Tb4b4b4)Tb4b4b4:
Te6edf3coro Tff7b72= Te6edf3asyncioTff7b72.Td2a8ffcoroutineTb4b4b4(Tff7b72lambdaTb4b4b4: Tff7b72NoneTb4b4b4)Tb4b4b4(Tb4b4b4)
Te6edf3real_run_asyncTb4b4b4(Te6edf3coroTb4b4b4)
Tff7b72finallyTb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ffmain_loop Tff7b72= Te6edf3real_main_loop

Te6edf3remaining Tff7b72= Te6edf3AsyncUtilsTff7b72.Td2a8ff_pending_futures
Tff7b72assert Te6edf3done Tff7b72not Tff7b72in Te6edf3remaining
Tff7b72assert Te6edf3pending Tff7b72in Te6edf3remaining

Tff7b72def Td2a8fftest_no_loop_prints_warningTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3capsysTb4b4b4)Tb4b4b4:
Te6edf3real_main_loop Tff7b72= Te6edf3AsyncUtilsTff7b72.Td2a8ffmain_loop
Te6edf3real_run_async Tff7b72= Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTff7b72.Td2a8ff__func__
Tff7b72tryTb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ffmain_loop Tff7b72= Tff7b72None
Te6edf3coro Tff7b72= Te6edf3asyncioTff7b72.Td2a8ffcoroutineTb4b4b4(Tff7b72lambdaTb4b4b4: Tff7b72NoneTb4b4b4)Tb4b4b4(Tb4b4b4)
Te6edf3real_run_asyncTb4b4b4(Te6edf3coroTb4b4b4)
Tff7b72finallyTb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ffmain_loop Tff7b72= Te6edf3real_main_loop

Te6edf3captured Tff7b72= Te6edf3capsysTff7b72.Td2a8ffreadouterrTb4b4b4(Tb4b4b4)
Tff7b72assert Ta5d6ff"Ta5d6ffWARNINGTa5d6ff" Tff7b72in Te6edf3capturedTff7b72.Td2a8ffout


T8b949e# ---------------------------------------------------------------------------
T8b949e# websocket_broadcast dead-client pruning
T8b949e# ---------------------------------------------------------------------------


Tff7b72class T56d364TestWebsocketBroadcastPruningTb4b4b4:
Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tff7b72async Tff7b72def Td2a8fftest_dead_clients_removed_on_send_failureTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787meshchat Tff7b72import Te6edf3ReticulumMeshChat

Te6edf3good_ws Tff7b72= Te6edf3AsyncMockTb4b4b4(Tb4b4b4)
Te6edf3bad_ws Tff7b72= Te6edf3AsyncMockTb4b4b4(Tb4b4b4)
Te6edf3bad_wsTff7b72.Td2a8ffsend_strTff7b72.Td2a8ffside_effect Tff7b72= Tf85149ConnectionResetErrorTb4b4b4(Ta5d6ff"Ta5d6ffgoneTa5d6ff"Tb4b4b4)

Te6edf3app Tff7b72= Te6edf3MagicMockTb4b4b4(Te6edf3specTff7b72=Te6edf3ReticulumMeshChatTb4b4b4)
Te6edf3appTff7b72.Td2a8ffwebsocket_clients Tff7b72= Tb4b4b4[Te6edf3good_wsTb4b4b4, Te6edf3bad_wsTb4b4b4]

Tff7b72await Te6edf3ReticulumMeshChatTff7b72.Td2a8ffwebsocket_broadcastTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff'Ta5d6ff{Ta5d6ff"Ta5d6fftypeTa5d6ff"Ta5d6ff:Ta5d6ff"Ta5d6ffpingTa5d6ff"Ta5d6ff}Ta5d6ff'Tb4b4b4)

Te6edf3good_wsTff7b72.Td2a8ffsend_strTff7b72.Td2a8ffassert_called_once_withTb4b4b4(Ta5d6ff'Ta5d6ff{Ta5d6ff"Ta5d6fftypeTa5d6ff"Ta5d6ff:Ta5d6ff"Ta5d6ffpingTa5d6ff"Ta5d6ff}Ta5d6ff'Tb4b4b4)
Te6edf3bad_wsTff7b72.Td2a8ffsend_strTff7b72.Td2a8ffassert_called_onceTb4b4b4(Tb4b4b4)
Tff7b72assert Te6edf3bad_ws Tff7b72not Tff7b72in Te6edf3appTff7b72.Td2a8ffwebsocket_clients
Tff7b72assert Te6edf3good_ws Tff7b72in Te6edf3appTff7b72.Td2a8ffwebsocket_clients

Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tff7b72async Tff7b72def Td2a8fftest_all_healthy_clients_keptTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787meshchat Tff7b72import Te6edf3ReticulumMeshChat

Te6edf3clients Tff7b72= Tb4b4b4[Te6edf3AsyncMockTb4b4b4(Tb4b4b4) Tff7b72for Te6edf3_ Tff7b72in Tffa657rangeTb4b4b4(T79c0ff3Tb4b4b4)Tb4b4b4]
Te6edf3app Tff7b72= Te6edf3MagicMockTb4b4b4(Te6edf3specTff7b72=Te6edf3ReticulumMeshChatTb4b4b4)
Te6edf3appTff7b72.Td2a8ffwebsocket_clients Tff7b72= Tffa657listTb4b4b4(Te6edf3clientsTb4b4b4)

Tff7b72await Te6edf3ReticulumMeshChatTff7b72.Td2a8ffwebsocket_broadcastTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff'Ta5d6ff{Ta5d6ff"Ta5d6ffokTa5d6ff"Ta5d6ff:true}Ta5d6ff'Tb4b4b4)

Tff7b72assert Tffa657lenTb4b4b4(Te6edf3appTff7b72.Td2a8ffwebsocket_clientsTb4b4b4) Tff7b72== T79c0ff3
Tff7b72for Te6edf3c Tff7b72in Te6edf3clientsTb4b4b4:
Te6edf3cTff7b72.Td2a8ffsend_strTff7b72.Td2a8ffassert_called_onceTb4b4b4(Tb4b4b4)

Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tff7b72async Tff7b72def Td2a8fftest_all_dead_clients_clearedTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787meshchat Tff7b72import Te6edf3ReticulumMeshChat

Te6edf3clients Tff7b72= Tb4b4b4[Te6edf3AsyncMockTb4b4b4(Tb4b4b4) Tff7b72for Te6edf3_ Tff7b72in Tffa657rangeTb4b4b4(T79c0ff3Tb4b4b4)Tb4b4b4]
Tff7b72for Te6edf3c Tff7b72in Te6edf3clientsTb4b4b4:
Te6edf3cTff7b72.Td2a8ffsend_strTff7b72.Td2a8ffside_effect Tff7b72= Tf85149BrokenPipeError

Te6edf3app Tff7b72= Te6edf3MagicMockTb4b4b4(Te6edf3specTff7b72=Te6edf3ReticulumMeshChatTb4b4b4)
Te6edf3appTff7b72.Td2a8ffwebsocket_clients Tff7b72= Tffa657listTb4b4b4(Te6edf3clientsTb4b4b4)

Tff7b72await Te6edf3ReticulumMeshChatTff7b72.Td2a8ffwebsocket_broadcastTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffdataTa5d6ff"Tb4b4b4)

Tff7b72assert Tffa657lenTb4b4b4(Te6edf3appTff7b72.Td2a8ffwebsocket_clientsTb4b4b4) Tff7b72== T79c0ff0


T8b949e# ---------------------------------------------------------------------------
T8b949e# _telemetry_no_location_warned cap
T8b949e# ---------------------------------------------------------------------------


Tff7b72class T56d364TestTelemetryWarnedCapTb4b4b4:
Tff7b72def Td2a8fftest_cap_at_256_then_clearedTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3warned Tff7b72= Tffa657setTb4b4b4(Tb4b4b4)
Tff7b72for Te6edf3i Tff7b72in Tffa657rangeTb4b4b4(T79c0ff256Tb4b4b4)Tb4b4b4:
Tff7b72if Tffa657lenTb4b4b4(Te6edf3warnedTb4b4b4) Tff7b72>Tff7b72= T79c0ff256Tb4b4b4:
Te6edf3warnedTff7b72.Td2a8ffclearTb4b4b4(Tb4b4b4)
Te6edf3warnedTff7b72.Td2a8ffaddTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffhash_Tffd700{Te6edf3iTffd700}Ta5d6ff"Tb4b4b4)

Tff7b72assert Tffa657lenTb4b4b4(Te6edf3warnedTb4b4b4) Tff7b72== T79c0ff256

Tff7b72if Tffa657lenTb4b4b4(Te6edf3warnedTb4b4b4) Tff7b72>Tff7b72= T79c0ff256Tb4b4b4:
Te6edf3warnedTff7b72.Td2a8ffclearTb4b4b4(Tb4b4b4)
Te6edf3warnedTff7b72.Td2a8ffaddTb4b4b4(Ta5d6ff"Ta5d6ffhash_256Ta5d6ff"Tb4b4b4)

Tff7b72assert Tffa657lenTb4b4b4(Te6edf3warnedTb4b4b4) Tff7b72== T79c0ff1
Tff7b72assert Ta5d6ff"Ta5d6ffhash_256Ta5d6ff" Tff7b72in Te6edf3warned


T8b949e# ---------------------------------------------------------------------------
T8b949e# Integration: state updates don't bloat memory with attachment re-encoding
T8b949e# ---------------------------------------------------------------------------


Tff7b72class T56d364TestStateUpdateMemoryTb4b4b4:
Tff7b72def Td2a8fftest_state_update_avoids_large_fields_writeTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3tmp_pathTb4b4b4)Tb4b4b4:
T8b949e"""Verify that update_lxmf_message_state does NOT cause the provider
to receive the large fields blob that upsert_lxmf_message does."""
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787database Tff7b72import Te6edf3Database

Te6edf3db Tff7b72= Te6edf3DatabaseTb4b4b4(Tffa657strTb4b4b4(Te6edf3tmp_path Tff7b72/ Ta5d6ff"Ta5d6fft.dbTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3dbTff7b72.Td2a8ffinitializeTb4b4b4(Tb4b4b4)

Te6edf3big_attachment Tff7b72= Ta5d6ff"Ta5d6ffXTa5d6ff" Tff7b72* T79c0ff100_000
Te6edf3fields Tff7b72= Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffimageTa5d6ff"Tb4b4b4: Tb4b4b4{Ta5d6ff"Ta5d6ffimage_bytesTa5d6ff"Tb4b4b4: Te6edf3big_attachmentTb4b4b4}Tb4b4b4}Tb4b4b4)
Te6edf3msg Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffmsg1Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffsource_hashTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffdTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffpeer_hashTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffstateTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffoutboundTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffprogressTa5d6ff"Tb4b4b4: T79c0ff0.0Tb4b4b4,
Ta5d6ff"Ta5d6ffis_incomingTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffmethodTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffdirectTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdelivery_attemptsTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffnext_delivery_attempt_atTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6fftitleTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fffieldsTa5d6ff"Tb4b4b4: Te6edf3fieldsTb4b4b4,
Ta5d6ff"Ta5d6fftimestampTa5d6ff"Tb4b4b4: T79c0ff1Tb4b4b4,
Ta5d6ff"Ta5d6ffrssiTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffsnrTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffqualityTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffis_spamTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffreply_to_hashTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffattachments_strippedTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Tb4b4b4}
Te6edf3dbTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffupsert_lxmf_messageTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3gcTff7b72.Td2a8ffcollectTb4b4b4(Tb4b4b4)
Tff7b72import T7ee787os
Tff7b72import T7ee787psutil

Te6edf3mem_before Tff7b72= Te6edf3psutilTff7b72.Td2a8ffProcessTb4b4b4(Te6edf3osTff7b72.Td2a8ffgetpidTb4b4b4(Tb4b4b4)Tb4b4b4)Tff7b72.Td2a8ffmemory_infoTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffrss

Tff7b72for Te6edf3_ Tff7b72in Tffa657rangeTb4b4b4(T79c0ff50Tb4b4b4)Tb4b4b4:
Te6edf3dbTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffupdate_lxmf_message_stateTb4b4b4(
Te6edf3message_hashTff7b72=Ta5d6ff"Ta5d6ffmsg1Ta5d6ff"Tb4b4b4,
Te6edf3stateTff7b72=Ta5d6ff"Ta5d6ffsendingTa5d6ff"Tb4b4b4,
Te6edf3progressTff7b72=T79c0ff50.0Tb4b4b4,
Te6edf3delivery_attemptsTff7b72=T79c0ff1Tb4b4b4,
Te6edf3next_delivery_attempt_atTff7b72=Tff7b72NoneTb4b4b4,
Tb4b4b4)

Te6edf3gcTff7b72.Td2a8ffcollectTb4b4b4(Tb4b4b4)
Te6edf3mem_after Tff7b72= Te6edf3psutilTff7b72.Td2a8ffProcessTb4b4b4(Te6edf3osTff7b72.Td2a8ffgetpidTb4b4b4(Tb4b4b4)Tb4b4b4)Tff7b72.Td2a8ffmemory_infoTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffrss
Te6edf3delta_mb Tff7b72= Tb4b4b4(Te6edf3mem_after Tff7b72- Te6edf3mem_beforeTb4b4b4) Tff7b72/ Tb4b4b4(T79c0ff1024 Tff7b72* T79c0ff1024Tb4b4b4)

Tff7b72assert Te6edf3delta_mb Tff7b72< T79c0ff5.0Tb4b4b4, Tb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffState-only updates grew memory by Tffd700{Te6edf3delta_mbTffd700:Ta5d6ff.1fTffd700}Ta5d6ff MB; Ta5d6ff"
Ta5d6ff"Ta5d6ffthey should not re-serialize attachment dataTa5d6ff"
Tb4b4b4)

Te6edf3row Tff7b72= Te6edf3dbTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffget_lxmf_message_by_hashTb4b4b4(Ta5d6ff"Ta5d6ffmsg1Ta5d6ff"Tb4b4b4)
Tff7b72assert Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffstateTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6ffsendingTa5d6ff"
Te6edf3stored Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadsTb4b4b4(Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6fffieldsTa5d6ff"Tb4b4b4]Tb4b4b4)
Tff7b72assert Te6edf3storedTb4b4b4[Ta5d6ff"Ta5d6ffimageTa5d6ff"Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffimage_bytesTa5d6ff"Tb4b4b4] Tff7b72== Te6edf3big_attachment

Te6edf3dbTff7b72.Td2a8ffclose_allTb4b4b4(Tb4b4b4)


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────